package com.zhike.service.store;

import java.io.ByteArrayInputStream;
import java.io.InputStream;

import javax.annotation.PostConstruct;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import com.aliyun.oss.ClientBuilderConfiguration;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.ServiceException;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;
import com.google.gson.Gson;
import com.zhike.common.exception.ZkimException;
import com.zhike.common.utils.FileHelper;
import com.zhike.service.system.ISystemConfigService;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import lombok.extern.slf4j.Slf4j;

@Component
@Slf4j
public class Alioss {
    private OSS ossClient = null;

    @Autowired
    private ISystemConfigService systemConfigService;

    private String bucketName;

    private String domain;

    @PostConstruct
    public void initOSS() {
        if (ossClient == null) {
            synchronized (OSSClient.class) {
                if (ossClient == null) {
                    ClientBuilderConfiguration conf = new ClientBuilderConfiguration();
                    conf.setSocketTimeout(600000);

                    String aliyunConfigJsonStr = systemConfigService.getValueByKey("storage", "aliyun");
                    if (StringUtils.isNotBlank(aliyunConfigJsonStr)) {
                        AliStorage storage = new Gson().fromJson(aliyunConfigJsonStr, AliStorage.class);
                        bucketName = storage.getBucket();
                        domain = storage.getDomain();
                        ossClient = new OSSClientBuilder().build(storage.getEndpoint(), storage.getAccessKey(),
                                storage.getSecretKey(), conf);
                    }

                }
            }
        }
    }

    public void createDirectory(String directoryName) {
        try {
            String content = "";

            // 创建PutObjectRequest对象。
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, directoryName + "/",
                    new ByteArrayInputStream(content.getBytes()));
            ossClient.putObject(putObjectRequest);
        } catch (OSSException e) {
            log.error("requestId:{} hostId:{} err:{}", e.getRequestId(), e.getHostId(), e.getErrorMessage());
        }
    }

    /**
     * 基础文件上传
     * 
     * @param file
     * @param prefix
     * @return
     */
    public String upload(MultipartFile file, String prefix, String md5) {
        prefix = "im3" + prefix;
        try {
            createDirectory(prefix);
            return putFile(getKey(prefix, md5, FileHelper.getSuffix(file.getOriginalFilename())),
                    file.getInputStream(), file.getSize());
        } catch (Exception e) {
            e.printStackTrace();
            throw new ZkimException("打开文件失败：" + e.getMessage());
        }

    }

    public void deleteFile(String filepath) {
        try {
            ossClient.deleteObject(bucketName, filepath);
        } catch (OSSException e) {
            log.error("requestId:{} hostId:{} err:{}", e.getRequestId(), e.getHostId(), e.getErrorMessage());
        } catch (ClientException ce) {
            log.error("Error Message: {}", ce.getMessage());
        }
    }

    /**
     * 上传文件<>基础方法</>
     *
     * @param key         文件key
     * @param inputStream 输入流
     * @return
     */
    private String putFile(String key, InputStream inputStream, long fileSize) {
        if (inputStream == null) {
            throw new ServiceException("文件不能为空");
        }

        // 上传文件最大值 MB->bytes
        Integer maxsizeConf = systemConfigService.getValueByKeyAsInt("uploader", "maxsize");
        long maxSize = maxsizeConf * 1024 * 1024;

        if (fileSize <= 0 || fileSize > maxSize) {
            throw new ServiceException("请检查文件大小");
        }
        PutObjectResult result = null;
        try {
            // 创建上传Object的Metadata
            ObjectMetadata meta = new ObjectMetadata();
            // 被下载时网页的缓存行为
            meta.setCacheControl("no-cache");
            PutObjectRequest request = new PutObjectRequest(bucketName, key, inputStream, meta);
            result = ossClient.putObject(request);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage(), e);
        }
        if (result != null) {
            return getUrl(key);
        }
        return null;
    }

    /**
     * 获取上传文件的key
     * 上传和删除时除了需要bucketName外还需要此值
     *
     * @param prefix   前缀（非必传），可以用于区分是哪个模块或子项目上传的文件,默认 file 文件夹
     * @param fileName 文件名称（非必传），如果为空默认生成文件名，格式：yyyyMMdd-UUID
     * @param suffix   后缀 , 可以是 png jpg
     * @return
     */
    private static String getKey(final String prefix, final String fileName, final String suffix) {
        StringBuffer keySb = new StringBuffer();
        // 前缀处理
        if (!StringUtils.isEmpty(prefix)) {
            keySb.append(prefix).append("/");
        } else {
            keySb.append("tmp_type");
        }
        // 文件名处理
        if (StringUtils.isBlank(fileName)) {
            // 上传时间 因为后期可能会用 - 将key进行split，然后进行分类统计
            keySb.append(DateUtil.today());
            keySb.append("-");
            // 生成uuid
            keySb.append(IdUtil.simpleUUID());
        } else {
            keySb.append(fileName);
        }
        // 后缀处理
        if (StringUtils.isBlank(suffix)) {
            throw new NullPointerException("文件后缀不能为空");
        }
        if (suffix.contains(".")) {
            keySb.append(suffix.substring(suffix.lastIndexOf(".")));
        } else {
            keySb.append("." + suffix);
        }
        return keySb.toString();
    }

    /**
     * 根据key生成文件的访问地址
     *
     * @param key
     * @return
     */
    public String getUrl(String key) {
        // 拼接文件访问路径。由于拼接的字符串大多为String对象，而不是""的形式，所以直接用+拼接的方式没有优势
        StringBuffer url = new StringBuffer();
        url.append("http://")
                // .append(bucketName)
                // .append(".")
                .append(domain)
                .append("/").append(key);
        return url.toString();
    }
}
